/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2010 huliqing, huliqing.cn@gmail.com
 *
 * This file is part of QFaces.
 * QFaces is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QFaces is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with QFaces.  If not, see <http://www.gnu.org/licenses/>.
 *
 * 这个文件是QFaces的一部分。
 * 您可以单独使用或分发这个文件，但请不要移除这个头部声明信息.
 * QFaces是一个自由软件，您可以自由分发、修改其中的源代码或者重新发布它，
 * 新的任何修改后的重新发布版必须同样在遵守LGPL3或更后续的版本协议下发布.
 * 关于LGPL协议的细则请参考COPYING、COPYING.LESSER文件，
 * 您可以在QFaces的相关目录中获得LGPL协议的副本，
 * 如果没有找到，请连接到 http://www.gnu.org/licenses/ 查看并获取副本。
 *
 * - Author: Huliqing
 * - Contact: huliqing.cn@gmail.com
 * - License: GNU Lesser General Public License (LGPL)
 * - Blog and source code availability: http://www.huliqing.name/
 */

package name.huliqing.qfaces.lifecycle;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import name.huliqing.qfaces.Constant;
import name.huliqing.qfaces.HttpWriter;
import name.huliqing.qfaces.QFaces;
import name.huliqing.qfaces.component.Ajax;

/**
 * @author huliqing
 * @version 1.2
 * huliqing@live.com
 * last modify:20100622
 */
public class RestoreViewPhase {

    private final static Logger logger = Logger.getLogger(RestoreViewPhase.class.getName());
    private static final String WEBAPP_ERROR_PAGE_MARKER =
            "javax.servlet.error.message";

    public void doPhase(FacesContext context) throws FacesException {
//        long start = System.currentTimeMillis();
        try {
            execute(context);
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
        }
//        logger.fine("Restore view execute use time:" + 
//                (System.currentTimeMillis() - start));
    }

    public void execute(FacesContext fc) throws FacesException {
        if (null == fc) {
            throw new NullPointerException(this.getClass().getName());
        }

        // If an app had explicitely set the tree in the context, use that;
        UIViewRoot viewRoot = fc.getViewRoot();

//        if (viewRoot != null) {
//        	logger.info("If an app had explicitely set the tree in the context, use that;If an app had explicitely set the tree in the context, use that;" +
//        			"If an app had explicitely set the tree in the context, use that;If an app had explicitely set the tree in the context, use that;" +
//        			"If an app had explicitely set the tree in the context, use that;If an app had explicitely set the tree in the context, use that;" +
//        			"If an app had explicitely set the tree in the context, use that;If an app had explicitely set the tree in the context, use that;" +
//        			"If an app had explicitely set the tree in the context, use that;If an app had explicitely set the tree in the context, use that;" +
//        			"If an app had explicitely set the tree in the context, use that;If an app had explicitely set the tree in the context, use that;");
//            fc.getViewRoot().setLocale(fc.getExternalContext().getRequestLocale());
//            doAjaxRequest(fc, viewRoot);
//            return;
//        }

        // find view id
        String viewId = QFaces.getViewId(fc);
        if (viewId == null) {
            throw new FacesException("viewId not found,RestoreViewPhase, getViewId(fc)");
        }

        // Output javascript resources
        if (viewId.indexOf(Constant.PREFIX_RES_CSS) != -1) {
            String resCSS = viewId.substring(viewId.indexOf(Constant.PREFIX_RES_CSS) + Constant.PREFIX_RES_CSS.length());
            resCSS = resCSS.substring(0, resCSS.lastIndexOf(Constant.SUFFIX) - 1);
            HttpWriter writer = new HttpWriter(fc);
            writer.writeCSS(resCSS);
            return;
        }

        // Output javascript resources
        if (viewId.indexOf(Constant.PREFIX_RES_JAVASCRIPT) != -1) {
            String resJS = viewId.substring(viewId.indexOf(Constant.PREFIX_RES_JAVASCRIPT) + Constant.PREFIX_RES_JAVASCRIPT.length());
            resJS = resJS.substring(0, resJS.lastIndexOf(Constant.SUFFIX) - 1);
            HttpWriter writer = new HttpWriter(fc);
            writer.writeScript(resJS);
            return;
        }

        // Output image resources
        if (viewId.indexOf(Constant.PREFIX_RES_IMAGE) != -1) {
            String resImage = viewId.substring(viewId.indexOf(Constant.PREFIX_RES_IMAGE) + Constant.PREFIX_RES_IMAGE.length());
            resImage = resImage.substring(0, resImage.lastIndexOf(Constant.SUFFIX) - 1);
            HttpWriter writer = new HttpWriter(fc);
            writer.writeImg(resImage);
            return;
        }

        // QFaces Filter
        if (fc.getExternalContext().getRequestParameterMap().get(Constant.FILTER_NAME) != null) {
            doFilterRequest(fc);
            return;
        }

        // Check if is post back.
        boolean isPostBack = (isPostback(fc) && !isErrorPage(fc));
        if (isPostBack) {
            ViewHandler viewHandler = fc.getApplication().getViewHandler();
            viewRoot = viewHandler.restoreView(fc, viewId);
            if (viewRoot == null) {
                throw new FacesException("PostBase request,but viewRoot not found(viewHandler couldn't restore view)");
            }
            fc.setViewRoot(viewRoot);
        } else {
            viewRoot = fc.getApplication().getViewHandler().createView(fc, viewId);
            fc.setViewRoot(viewRoot);
        }

        // Do ajax invocation
        doAjaxRequest(fc, viewRoot);
    }

    // ------------------------------------------------------- Protected Methods
    /**
     * Do ajax request.
     * @param fc
     * @param uic
     */
    private void doAjaxRequest(FacesContext fc, UIComponent uic) {
//    	logger.fine("do Ajax request start...");
        Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
        String clientId = params.get("clientId");
        String componentClass = params.get("componentClass");
        if (clientId != null && isPostback(fc)) {
            UIComponent ui = uic.findComponent(clientId);
            if (ui instanceof Ajax) {
                ((Ajax) ui).ajax(params, fc);
            }
        } else if (componentClass != null && !isPostback(fc)) {
            try {
                Class<?> clazz = Class.forName(componentClass);
                // 不要强制转换为 name.huliqing.qfaces.component.Ajax类型，
                // 这会造成某些类型的Exception捕捉不到
                Object o = clazz.newInstance();
                Method ajax = clazz.getMethod("ajax", Map.class, FacesContext.class);
                ajax.invoke(o, params, fc);
            } catch (ClassNotFoundException cnfe) {
                logger.log(Level.SEVERE, null, cnfe.getCause());
                QFaces.getResponse().responseError(fc, cnfe.getMessage(), 500);
            } catch (NoSuchMethodException nsme) {
                logger.log(Level.SEVERE, null, nsme.getCause());
                QFaces.getResponse().responseError(fc, nsme.getMessage(), 500);
            } catch (InstantiationException ie) {
                logger.log(Level.SEVERE, null, ie.getCause());
                QFaces.getResponse().responseError(fc, ie.getMessage(), 500);
            } catch (IllegalAccessException iae) {
                logger.log(Level.SEVERE, null, iae.getCause());
                QFaces.getResponse().responseError(fc, iae.getMessage(), 500);
            } catch (IllegalArgumentException iae) {
                logger.log(Level.SEVERE, null, iae.getCause());
                QFaces.getResponse().responseError(fc, iae.getMessage(), 500);
            } catch (InvocationTargetException ite) {
                logger.log(Level.SEVERE, null, ite.getCause());
                QFaces.getResponse().responseError(fc, ite.getMessage(), 500);
            }
        } else {
            logger.severe("Error:缺少参数：\"clientId\" 或 \"componentClass\"作为ajax请求参数 ");
        }
    }

    /**
     * Do filter output
     * @param fc
     */
    private void doFilterRequest(FacesContext fc) {
        Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
        String componentClass = params.get("componentClass");
        if (componentClass == null) {
            throw new NullPointerException("As a filter request, but \"componentClass\" not found!");
        }
        try {
            Class<?> clazz = Class.forName(componentClass);
            Object o = clazz.newInstance();
            Method filter = clazz.getMethod("filter", Map.class, FacesContext.class);
            filter.invoke(o, params, fc);
        } catch (ClassNotFoundException cnfe) {
            logger.log(Level.SEVERE, null, cnfe.getCause());
            QFaces.getResponse().responseError(fc, cnfe.getMessage(), 500);
        } catch (NoSuchMethodException nsme) {
            logger.log(Level.SEVERE, null, nsme.getCause());
            QFaces.getResponse().responseError(fc, nsme.getMessage(), 500);
        } catch (InstantiationException ie) {
            logger.log(Level.SEVERE, null, ie.getCause());
            QFaces.getResponse().responseError(fc, ie.getMessage(), 500);
        } catch (IllegalAccessException iae) {
            logger.log(Level.SEVERE, null, iae.getCause());
            QFaces.getResponse().responseError(fc, iae.getMessage(), 500);
        } catch (IllegalArgumentException iae) {
            logger.log(Level.SEVERE, null, iae.getCause());
            QFaces.getResponse().responseError(fc, iae.getMessage(), 500);
        } catch (InvocationTargetException ite) {
            logger.log(Level.SEVERE, null, ite.getCause());
            QFaces.getResponse().responseError(fc, ite.getMessage(), 500);
        }
    }

    // --------------------------------------------------------- Private Methods
    private boolean isPostback(FacesContext fc) {
        Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
        // "com.sun.faces.VIEW", 用于支持sun的组件树状态信息，for JSF1.1
        return (params.containsKey("javax.faces.ViewState") || params.containsKey("com.sun.faces.VIEW"));
    }

    private static boolean isErrorPage(FacesContext context) {
        return (context.getExternalContext().getRequestMap().
                get(WEBAPP_ERROR_PAGE_MARKER) != null);
    }
}
